package com.xxx.springboot.distributedsession;

import java.util.Objects;
import java.util.UUID;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 自定义分布式request
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class DistributedHttpRequest extends HttpServletRequestWrapper {

    /**
     * cookie的最大生命周期为关闭浏览器
     */
    private static final int COOKIE_TIME_LIVE = -1;
    /**
     * servlet上下文
     */
    private final ServletContext context;

    /**
     * request对应的response
     */
    private final HttpServletResponse response;
    /**
     * 分布式session对象
     */
    private DistributedHttpSession session;

    /**
     * cookie中存储的sessionId的key
     */
    private String sessionIdCookieName;

    /**
     * 会话最大活动时间，单位秒
     */
    private int maxInactiveInterval = 0;

    /**
     * session id cookie domain
     */
    private String cookieDomain;

    /**
     * session id cookie path
     */
    private String cookieContextPath;

    /**
     * @param request  原生请求
     * @param response 原生响应
     * @param context  上下文
     */
    public DistributedHttpRequest(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
        super(request);
        this.response = response;
        this.context = context;
    }

    @Override
    public HttpSession getSession(boolean create) {
        return doGetSession(create);
    }

    @Override
    public HttpSession getSession() {
        return doGetSession(true);
    }

    /**
     * 获取当前的DistributedHttpSession实例。
     *
     * @return　DistributedHttpSession实例。
     */
    public DistributedHttpSession currentSession() {
        return session;
    }

    private HttpSession doGetSession(boolean create) {
        if (null == session) {
            Cookie cookie = CookieUtil.findCookie(this, sessionIdCookieName);
            if (null == cookie || StringUtils.isEmpty(cookie.getValue())) {
                //cookie中没有session id,则根据create参数创建新session
                session = buildHttpSession(create);
            } else {
                //根据session id 构建session对象
                session = buildHttpSession(cookie.getValue(), false);
            }
        }

        if (null != session) {
            //处理session已经失效的情况
            if (session.isInvalid()) {
                session.invalidate();
                session = buildHttpSession(create);
            }
            Objects.requireNonNull(session).access();
        }

        return session;
    }

    /**
     * 构建session对象
     *
     * @param create 如果获取不到session，是否新建一个
     * @return session对象 or null
     */
    private DistributedHttpSession buildHttpSession(boolean create) {
        if (create) {
            String sessionId = generateSessionId();
            return buildHttpSession(sessionId, true);
        }
        return null;
    }

    /**
     * 通过给定的session id构建session
     *
     * @param sessionId 会话id.
     * @param cookie    是否更新cookie值。true更新，false不更新。
     * @return session对象
     */
    private DistributedHttpSession buildHttpSession(String sessionId, boolean cookie) {
        DistributedHttpSession distributedHttpSession = new DistributedHttpSession(context, sessionId);
        //设置会话最大活动时间
        distributedHttpSession.setMaxInactiveInterval(maxInactiveInterval);
        distributedHttpSession.init();

        if (cookie) {
            CookieUtil.addCookie(
                    this,
                    response,
                    sessionIdCookieName,
                    sessionId,
                    cookieDomain,
                    cookieContextPath,
                    COOKIE_TIME_LIVE);
        }
        return distributedHttpSession;
    }

    /**
     * 以UUID的策略生成一个32长度的字符串，在同一时空中保持唯一。
     *
     * @return UUID128位长度字符串。
     */
    private String generateSessionId() {
        String id = UUID.randomUUID().toString();
        id = id.replace("-", "");
        return id;
    }
}
